package com.example.Windturbine.SimilarLearning;

import com.alibaba.excel.EasyExcel;
import com.example.Windturbine.Controller.ViewObject.Excel.PartExcel;
import com.example.Windturbine.Listener.ExcelListener;
import com.example.Windturbine.SimilarLearning.LearningDO.Sample;
import com.example.Windturbine.SimilarLearning.LearningDO.TrainDO;
import com.example.Windturbine.SimilarLearning.Listener.SampleListener;
import com.example.Windturbine.Utils.FileUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.xml.crypto.Data;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * @Author 丁永新
 * @Date 2022/1/5
 */
@Component
public class AutoAddDataUtil {

    @Bean
    @PostConstruct
    public BP getBpModel() {
        AutoAddDataUtil util=new AutoAddDataUtil();
        //输入层结点11个 输出层结点2个 学习率0.05
        BP bp = null;
        try {
            bp = util.getBP(11, 10, 2, 0.001,98);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //util.Test(bp);
        return bp;
    }


    //测试方法
    public void Test(BP bp) throws FileNotFoundException {
        //读取测试数据
        File file = new File(FileUtils.path + FileUtils.sep + "神经网络测试数据.xlsx");
        InputStream in = new FileInputStream(file);
        //读取数据集 并处理
        SampleListener sampleListener=new SampleListener(new ArrayList<TrainDO>());
        EasyExcel.read(in, Sample.class,sampleListener).sheet().doRead();
        List<TrainDO> samples = sampleListener.getTrainDOS();
        double rightNum=0d;
        System.out.println("BP神经网络开始预测");
        System.out.println("------------------------------");
        for(int i=0;i<samples.size();i++){

            //获取输入的11项参数
            double[] input=new double[11];
            input[0]=samples.get(i).getN1();
            input[1]=samples.get(i).getN2();
            input[2]=samples.get(i).getN3();
            input[3]=samples.get(i).getN4();
            input[4]=samples.get(i).getN5();
            input[5]=samples.get(i).getN6();
            input[6]=samples.get(i).getN7();
            input[7]=samples.get(i).getN8();
            input[8]=samples.get(i).getN9();
            input[9]=samples.get(i).getN10();
            input[10]=samples.get(i).getN11();

            //获取真实结果
            double[] tar=new double[2];
            tar[0]=samples.get(i).getYes();
            tar[1]=samples.get(i).getNo();
            boolean res=tar[0]>tar[1]?true:false;

            //获取预测结果
            double[] output=new double[2];
            bp.predict(input,output);
            boolean pre=output[0]>=output[1]?true:false;


            double error=Math.max(Math.abs(tar[0]-output[0]),(Math.abs(tar[1]-output[1])));
            //真实和预测的比较
            System.out.println("BP神经网络第"+(i+1)+"次预测");
            System.out.println("预测结果："+pre+" 真实结果："+res+" 本次预测的误差："+error);
            rightNum+=error;
        }

        //统计正确率
        System.out.println("------------------------------");
        System.out.println("测试完毕！");
        System.out.println("本次神经网络预测正确率："+(samples.size()-rightNum)*100/samples.size()+"%");
    }


    public BP getBP(int input_node, int hide1_node, int out_node, double rate,double rightRate) throws FileNotFoundException {
        //初始化BP一个未训练的BP神经网络
        BP bp=new BP(input_node, hide1_node, out_node, rate);
        //准备训练数据和结果
        File file = new File(FileUtils.path + FileUtils.sep + "神经网络训练数据.xlsx");
        InputStream in = new FileInputStream(file);
        //读取数据集 并处理
        SampleListener sampleListener=new SampleListener(new ArrayList<TrainDO>());
        EasyExcel.read(in, Sample.class,sampleListener).sheet().doRead();
        List<TrainDO> samples = sampleListener.getTrainDOS();
        //把训练集分开为输入和输出
        int num=1;
        long begin = System.currentTimeMillis();
        while(true){
            double error=0;
            for(int i=0;i<samples.size();i++){
                double[] input=new double[11];
                input[0]=samples.get(i).getN1();
                input[1]=samples.get(i).getN2();
                input[2]=samples.get(i).getN3();
                input[3]=samples.get(i).getN4();
                input[4]=samples.get(i).getN5();
                input[5]=samples.get(i).getN6();
                input[6]=samples.get(i).getN7();
                input[7]=samples.get(i).getN8();
                input[8]=samples.get(i).getN9();
                input[9]=samples.get(i).getN10();
                input[10]=samples.get(i).getN11();

                double[] output=new double[2];
                output[0]=samples.get(i).getYes();
                output[1]=samples.get(i).getNo();
                bp.train(input,output);
                error+=bp.getError();
            }
            double RRate=(samples.size()-error)*100/samples.size();
            System.out.println("神经网络 第"+num+"次训练，准确率为"+RRate);
            num++;
            if(RRate>rightRate){
                long end = System.currentTimeMillis();
                System.out.println("误差达到目标值，训练结束！");
                System.out.println("训练次数："+num+" 训练时间"+(end-begin)+"毫秒");
                return bp;//返回一个训练好的BP神经网络
            }
        }
    }


    //计算莱文斯坦距离
    public int  minDistance(String word1, String word2) {
        if (word1.length() == 0 || word2.length() == 0)
            return word1.length() == 0 ? word2.length() : word1.length();
        int[][] arr = new int[word1.length() + 1][word2.length() + 1];
        for (int i = 0; i <= word1.length(); i++) {
            arr[i][0] = i;
        }
        for (int j = 0; j <= word2.length(); j++) {
            arr[0][j] = j;
        }
        for (int i = 0; i < word1.length(); i++) {
            for (int j = 0; j < word2.length(); j++) {
                if (word1.charAt(i) == word2.charAt(j)) {
                    arr[i + 1][j + 1] = arr[i][j];
                } else {
                    int replace = arr[i][j]+1 ;
                    int insert = arr[i][j + 1]+1 ;
                    int delete = arr[i + 1][j]+1 ;
                    int min = replace > insert ? insert : replace;
                    min = delete > min ? min : delete;
                    arr[i + 1][j + 1] = min;
                }
            }
        }
        return arr[word1.length()][word2.length()];
    }

    //计算文本数据的相似度
    public double numHandle(BigDecimal aim,BigDecimal src){
        double d1=aim.doubleValue();  //把BigDecimal转成double类型
        double d2=src.doubleValue();  //把BigDecimal转成double类型
        //BigDecimal不会舍弃精度，为了避免无限小数的情况此处保留两位有效数字
        BigDecimal divide = aim.subtract(src).divide(new BigDecimal(Math.max(d1,d2)), 2, BigDecimal.ROUND_HALF_DOWN);
        divide=divide.abs();//取绝对值
        double v = divide.doubleValue();
        return v;
    }


    //归一化处理
    public double Normalized(String word1, String word2,int minDistances){
        int maxLength=Math.max(word1.length(),word2.length());
        if(maxLength==0) return 1;
        double result = (maxLength - minDistances) / maxLength;
        return result;
    }


    //计算处理后的文本数据
    public double wordHandle(String word1, String word2){
        return Normalized(word1,word2,minDistance(word1,word2));
    }

}
